﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Routing;
using Autofac;
using Web7.Core.Sites;
using Web7.Core.Models;
using Web7.Core.Security;
using System.Collections.Concurrent;
using Web7.Ioc;

namespace Web7.Core
{
    /// <summary>
    /// 请求上下文
    /// </summary>
    /// <remarks>
    /// <para>该类封装RequestContext、RouteCollection、Site和IUser。</para>
    /// <para>一般作为其他类的构造函数的参数传入。使用时自动被依赖注入容器实例化。</para>
    /// </remarks>
    public class Web7Context
    {
        public Web7Context(RequestContext requestContext, RouteCollection routes, Site site, IUser user)
        {
            HttpContext = requestContext.HttpContext;
            RequestContext = requestContext;
            Routes = routes;
            Site = site;
            User = user;

            string dataFormat = RequestContext.RouteData.Values["dataFormat"] as string;

            if (string.Compare(dataFormat, "RSS", true) == 0)
                RequestDataFormat = RequestDataFormat.RSS;
            else if (string.Compare(dataFormat, "ATOM", true) == 0)
                RequestDataFormat = RequestDataFormat.ATOM;
            else
                RequestDataFormat = RequestDataFormat.Web;
        }

        public Web7Context(Web7Context context)
            : this(context.RequestContext, context.Routes, context.Site, context.User)
        {
        }

        public HttpContextBase HttpContext { get; private set; }
        public RequestContext RequestContext { get; private set; }
        public RequestDataFormat RequestDataFormat { get; private set; }
        public RouteCollection Routes { get; private set; }
        public Site Site { get; private set; }
        public IUser User { get; set; }
    }

    public interface IWorkContextStateProvider : IDependency
    {
        Func<WorkContext, T> Get<T>(string name);
    }

    public abstract class WorkContext
    {
        public abstract T Resolve<T>();
        public abstract bool TryResolve<T>(out T service);

        public abstract T GetState<T>(string name);
        public abstract void SetState<T>(string name, T value);

        public HttpContextBase HttpContext
        {
            get { return GetState<HttpContextBase>("HttpContext"); }
            set { SetState("HttpContext", value); }
        }

        public dynamic Layout
        {
            get { return GetState<object>("Layout"); }
            set { SetState("Layout", value); }
        }

        public Site CurrentSite
        {
            get { return GetState<Site>("CurrentSite"); }
            set { SetState("CurrentSite", value); }
        }

        public IUser CurrentUser
        {
            get { return GetState<IUser>("CurrentUser"); }
            set { SetState("CurrentUser", value); }
        }

        public string CurrentCulture
        {
            get { return GetState<string>("CurrentCulture"); }
            set { SetState("CurrentCulture", value); }
        }
    }

    class WorkContextImplementation : WorkContext
    {
        private readonly IComponentContext _componentContext;

        private readonly ConcurrentDictionary<string, Func<object>> _stateResolvers = new ConcurrentDictionary<string, Func<object>>();

        readonly IEnumerable<IWorkContextStateProvider> _workContextStateProviders;

        public WorkContextImplementation(IComponentContext componentContext)
        {
            _componentContext = componentContext;
            _workContextStateProviders = componentContext.Resolve<IEnumerable<IWorkContextStateProvider>>();
        }

        public override T Resolve<T>()
        {
            return _componentContext.Resolve<T>();
        }

        public override bool TryResolve<T>(out T service)
        {
            return _componentContext.TryResolve(out service);
        }

        public override T GetState<T>(string name)
        {
            var resolver = _stateResolvers.GetOrAdd(name, FindResolverForState<T>);
            return (T)resolver();
        }

        Func<object> FindResolverForState<T>(string name)
        {
            var resolver = Enumerable.FirstOrDefault(_workContextStateProviders.Select(wcsp => wcsp.Get<T>(name)), value => !Equals(value, default(T)));

            if (resolver == null)
            {
                return () => default(T);
            }
            return () => resolver(this);
        }


        public override void SetState<T>(string name, T value)
        {
            _stateResolvers[name] = () => value;
        }
    }
}
